import Vue from 'vue'
import VueRouter from 'vue-router'
import Vuex from 'vuex'
import $ from 'jquery'
import App from './App.vue'
import NewsInfo from './components/NewsInfo'
import NewsList from './components/NewsList'
import CommentWatcher from './components/CommentWatcher'

import BootstrapVue from 'bootstrap-vue'

import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

Vue.use(BootstrapVue)


Vue.config.productionTip = false;
// Router fields [begin] {
const routes = [
  
  {
    path: '/demo/:newsId',
    component: NewsInfo,
  },
  {
    path: '/demo/',
    component: NewsList,
  },
  {
    path: '/admin/',
    component: CommentWatcher,
  },
  {
    path: '/',
    component: NewsList,
  },
];
const router = new VueRouter({
  routes
});
Vue.use(VueRouter);
// Router fields [begin] }

// Vuex fields [begin] {
Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    ajaxQuery: false,
    $socket: null,
    socketId: null,
    socketIdled: true,
    viewingNews: null,
    newsList: [],
    isEditing: false,
    editingNews: {
      title: '',
      description: '',
      date: new Date(),
    },
    commentList: [],
    verifingComment: null,
  },
  getters: {
    isValidToSendBySocket(state) {
      return null != state.$socket && null != state.socketId && state.socketIdled;
    },
    isValidToObtainAllComment(state) {
      return !state.ajaxQuery;
    },
    isValidToObtainSingleNews(state) {
      return !state.ajaxQuery;
    },
    isValidToObtainAllNews(state) {
      return !state.ajaxQuery && !state.isEditing;
    },
    isValidToCreateNews(state) {
      return !state.ajaxQuery && state.isEditing && state.editingNews.title.length > 0 && state.editingNews.description.length > 0;
    },
    isSocketEnabled(state) {
      return state.socketId != null;
    },
    findNewsById(state) {
      return (_id) => {
        return state.newsList && state.newsList.find(doc => doc._id == _id);
      }
    },
    appendComment(state) {
      return function(commentData, commentList=state.commentList) {
        let index = commentList.findIndex(x => x._id == commentData._id);
        if (-1 == index) {
          commentList.push(commentData);
        } else {
          const targetComment = commentList[index];
          if (
            new Date(targetComment.updatedAt).getTime() <
            new Date(commentData.updatedAt).getTime()
          ) {
            commentList.splice(index, 1, commentData);
          }
        }
      };
    },
  },
  mutations: {
    routeUpdate(state) {
      state.socketIdled = true;
      state.viewingNews = null;
      state.isEditing = false;
      state.verifingComment = null;
      this.commit('clearEditingNews');
    },
    clearEditingNews(state) {
      state.editingNews.title = '';
      state.editingNews.description = '';
      state.editingNews.date = new Date();
    },
    updateEditingNews(state, payload) {
      $.extend(state.editingNews, payload.data);
    },
    startEditing(state) {
      state.isEditing = true;
      this.commit('clearEditingNews');
    },
    endEditing(state) {
      state.isEditing = false;
      this.commit('clearEditingNews');
    },
    updateNewsList(state, payload) {
      state.newsList = payload.data;
    },
    updateViewingNews(state, payload) {
      state.viewingNews = payload.data;
    },
    createNews() {
      this.commit('endEditing');
    },
    appendNews(state, payload) {
      state.newsList.push(payload.data);
    },
    queryStart(state) {
      state.ajaxQuery = true;
    },
    querySucceed(state) {
      state.ajaxQuery = false;
    },
    queryFailed(state) {
      state.ajaxQuery = false;
    },
    setSocket(state, payload) {
      state.$socket = payload.data;
      state.socketIdled = true;
    },
    socketSending(state) {
      state.socketIdled = false;
    },
    socketSended(state) {
      state.socketIdled = true;
    },
    "socket:setId": function(state, payload) {
      state.socketId = payload.data;
    },
    "socket:disconnect": function(state) {
      state.socketId = null;
    },
    "socket:appendComment": function(state, payload) {
      const self = this;
      if (null != state.viewingNews) {
        if (payload._id != state.viewingNews._id) {
          return;
        }
        self.getters.appendComment(payload.data, state.viewingNews.comments);
      }
    },
    appendSingleComment(state, payload) {
      const self = this;
      self.getters.appendComment(payload.data);
    },
    appendCommentList(state, payload) {
      const self = this;
      payload.data.forEach(function(comment) {
        self.getters.appendComment(comment);
      });
    },
    setVerifingComment(state, payload) {
      state.verifingComment = payload;
    },
    "socket:updateComment"(state, {_id, data}) {
      const self = this;
      if (null != state.viewingNews && state.viewingNews._id == _id) {
        self.getters.appendComment(data, state.viewingNews.comments);
      }
      self.getters.appendComment(data);
    },
    "socket:createCommentSucceed"() {
      this.commit('socketSended');
    },
    "socket:verifyCommentSucceed"(state) {
      this.commit('socketSended');
      state.verifingComment = null;
    },
  },
  actions: {
    async createNews({ state, commit, getters }) {
      if (!getters.isValidToCreateNews) {
        return;
      }
      const savedNewsObject = {...state.editingNews};
      commit('createNews');
      try {
        commit('queryStart');
        const newsObject = await $.post('/demo/news/create', {
          news: $.extend({}, savedNewsObject, {date: savedNewsObject.date.toJSON()}),
        });
        commit('querySucceed');
        commit('appendNews', {
          data: newsObject,
        });
      } catch (e) {
        commit('queryFailed');
      }
    },
    async obtainAllNews({ commit, getters }) {
      if (!getters.isValidToObtainAllNews) {
        return;
      }
      try {
        commit('queryStart');
        const newsList = await $.get('/demo/news/obtainAll');
        commit('querySucceed');
        commit('updateNewsList', {
          data: newsList,
        });
      } catch (e) {
        commit('queryFailed');
      }
    },
    async obtainViewingNews({ state, commit, getters }, payload) {
      if (!getters.isValidToObtainSingleNews) {
        return;
      }
      const _id = payload.data;
      try {
        commit('queryStart');
        const news = await $.get('/demo/news/obtain', {
          _id: _id,
        });
        commit('querySucceed');
        commit('updateViewingNews', {
          data: news,
        });
        state.$socket.emit('viewingNews', { _id: state.viewingNews._id });
      } catch (e) {
        commit('queryFailed');
      }
    },
    createComment({ state, commit, getters }, { data }) {
      if (!getters.isValidToSendBySocket) {
        return;
      }
      commit('socketSending');
      state.$socket.emit('createComment', {
        _id: state.viewingNews._id,
        data,
        socketId: state.socketId,
      });
    },
    async obtainAllComment({ commit, getters }) {
      if (!getters.isValidToObtainAllComment) {
        return;
      }
      try {
        commit('queryStart');
        const commentList = await $.get('/demo/comment/obtainAll');
        commit('querySucceed');
        commit('appendCommentList', {
          data: commentList,
        });
      } catch (e) {
        commit('queryFailed');
      }
    },
    adminLogin({ getters, state }) {
      if (!getters.isValidToSendBySocket) {
        return;
      }
      state.$socket.emit('adminLogin');
    },
    "socket:newsIdRequired"({ getters, state }) {
      if (!getters.isValidToSendBySocket) {
        return;
      }
      if (null != state.viewingNews) {
        state.$socket.emit('viewingNews', { _id: state.viewingNews._id });
      }
    },
    async verifyComment({ getters, commit, state }, payload) {
      if (!getters.isValidToSendBySocket) {
        return;
      }
      commit('socketSending');
      commit('setVerifingComment', payload);
      state.$socket.emit('verifyComment', payload);
    },
  },
});
// Vuex fields [end] }

// socket fields [begin] {
import VueSocketIO from 'vue-socket.io'

Vue.use(new VueSocketIO({
  debug: true,
  connection: '/',
  vuex: {
    store,
    mutationPrefix: "socket:",
    actionPrefix: "socket:",
  },
}));
// socket fields [end] }

new Vue({
  router,
  store,
  render: h => h(App),
  sockets: {
    connect() {
      const vm = this;
      if (null != vm.$store.$socket) {
        return;
      }
      vm.$store.commit('setSocket', {
        data: vm.$socket,
      });
    },
    reconnect() {
      const vm = this;
      // Warning: reconnect之后似乎会触发connect。
      if (null != vm.$store.$socket) {
        return;
      }
      vm.$store.commit('setSocket', {
        data: vm.$socket,
      });
    },
    disconnect() {
      const vm = this;
      vm.$store.commit('setSocket', {
        data: null,
      });
    }
  },
  watch: {
    $route() {
      const vm = this;
      vm.$store.commit('routeUpdate');
    },
  }
}).$mount('#app')
